<!DOCTYPE html>
          <head>
        <meta charset="utf-8">
            
            <title>
                「Android」对App进行代码混淆 | ttdevs
            </title>
            <meta content="width=device-width, initial-scale=1" name="viewport">
            <meta name="theme-color" content="#4184f3">
            
            
            <link href="/favicon.ico" rel="icon"/>
            

            <link rel="stylesheet" href="/css/highlight.light.css">
            <link rel="stylesheet" href="/css/prism-customize.css">
            <link rel="stylesheet" href="/css/nav-icon.css">
            <link rel="stylesheet" href="/css/waves.min.css">
            <link rel="stylesheet" href="/css/jquery.tocify.css">
            <link rel="stylesheet" href="/css/main.css">
            <link rel="stylesheet" href="/css/nav-indicator.css">
            
  

  
  <!-- 谷歌统计 -->
  <script>
    (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
    (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
    m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
    })(window,document,'script','//www.google-analytics.com/analytics.js','ga');

    ga('create', 'UA-97465173-1', 'auto');
    ga('send', 'pageview');

  </script>
  
            </meta>
        </meta>
    </head>

    <body>
        <header>
            <!-- cover image or sth. -->
        </header>
        <div id="main" class="m-scene">
            
<div class="nav-wrapper">

    <div class="container">
        <nav>
            <div class="logo wave">
                <a href="/" id="logo">
                    ttdevs
                </a>
            </div>
            <div class="nav-toggle-icon" >
                <div class="material-hamburger">
                    <span>
                    </span>
                    <span>
                    </span>
                    <span>
                    </span>
                </div>
            </div>
            <div class="menu-wrapper">
                <div class="nav-indicator">
                </div>
                <ul class="menus">
                    
                     
                        <li>
                            <a class="wave " href="/">
                                首页
                            </a>
                        </li>
                     
                        <li>
                            <a class="wave " href="/archives">
                                归档
                            </a>
                        </li>
                     
                        <li>
                            <a class="wave " href="/about">
                                关于
                            </a>
                        </li>
                     
                    
                   
                </ul>
            </div>
        </nav>
    </div>
</div>
            <div class="container content">
                <div class="scene_element scene_element--fadein">
                    <div class="row">
    <div class="main">
        <article>
          
          <header class="post-header with-cover" style="background-image:url('/1970/01/01/[Android]对App进行代码混淆/cover.jpg')" >
          
          </header>
          <h1 class="post-title">「Android」对App进行代码混淆</h1>

          <section class="post-info">
            <span class="post-date">1970/01/01</span>
            
            <span class="post-category">
                <a class="article-category-link" href="/categories/技术/">技术</a>
            </span>
            
            
            <span class="post-tags">
              <ul class="post-tag-list"><li class="post-tag-list-item"><a class="post-tag-list-link" href="/tags/Android/">Android</a></li></ul>
            </span>
            
          </section>

          <section class="post-content">
            <p>接到一个新的任务，对现有项目进行代码混淆。之前对混淆有过一些了解，但是不够详细和完整，知道有些东西混淆起来还是比较棘手的。不过幸好目前的项目不是太复杂（针对混淆这块来说），提前完成～～ 现总结之。</p>
<h2 id="0x01-第一部分"><a href="/1970/01/01/[Android]对App进行代码混淆/#0x01-第一部分" class="headerlink" title="0x01 第一部分"></a>0x01 第一部分</h2><p>介绍下操作流程（eclipse）：</p>
<ul>
<li><p>打开混淆器</p>
<p>  找到项目根目录下的 <code>project.properties</code> 文件<br>  <code>#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt</code><br>  将上面这行前的 <code>#</code> 删除即可</p>
</li>
<li><p>修改混淆配置文件</p>
<p>  找到项目根目录下的 <code>proguard-project.txt</code> 文件，修改其中代码，这部分是最关键</p>
</li>
<li><p>保存相关文件供以后出错时使用</p>
<p>  主要有导出的apk文件、项目根目录下的proguard目录下的文件（主要的是mapping.txt）和项目源码</p>
</li>
<li><p>项目运行过程出错处理</p>
<p>  根据错误信息和第3步中保存的mapping定位错误位置</p>
</li>
</ul>
<p>知道这些之后，我们对其进行展开。</p>
<p>打开eclipse然后新建一个项目，默认会创建 <code>proguard-project.txt</code> 和<code>project.properties</code>。编写我们的代码，修改 <code>proguard-project.txt</code> ：</p>
<p><code>#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt</code></p>
<p><code>:proguard-project.txt</code> </p>
<p>将这行前的 <code>#</code> 删除，最后导出即可实现对代码的混淆（即使我们没有去编写proguard-<br>project.txt中的内容）。下面是我的测试代码：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div><div class="line">53</div><div class="line">54</div><div class="line">55</div><div class="line">56</div><div class="line">57</div><div class="line">58</div><div class="line">59</div><div class="line">60</div><div class="line">61</div><div class="line">62</div><div class="line">63</div><div class="line">64</div><div class="line">65</div><div class="line">66</div><div class="line">67</div><div class="line">68</div><div class="line">69</div><div class="line">70</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">MainActivity</span> <span class="keyword">extends</span> <span class="title">Activity</span> </span>&#123;</div><div class="line"></div><div class="line">    <span class="keyword">private</span> String mName;</div><div class="line"></div><div class="line">    <span class="meta">@Override</span></div><div class="line">    <span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">onCreate</span><span class="params">(Bundle savedInstanceState)</span> </span>&#123;</div><div class="line">        <span class="keyword">super</span>.onCreate(savedInstanceState);</div><div class="line">        setContentView(R.layout.activity_main);</div><div class="line"></div><div class="line">        mName = <span class="string">"ttdevs"</span>;</div><div class="line"></div><div class="line">        getString(mName);</div><div class="line">        setName(mName);</div><div class="line">        showDialog();</div><div class="line">        <span class="comment">// testError();</span></div><div class="line">    &#125;</div><div class="line"></div><div class="line">    <span class="function"><span class="keyword">public</span> String <span class="title">getString</span><span class="params">(String name)</span> </span>&#123;</div><div class="line">        <span class="keyword">return</span> <span class="string">"hello "</span> + name;</div><div class="line">    &#125;</div><div class="line"></div><div class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setName</span><span class="params">(String name)</span> </span>&#123;</div><div class="line">        System.out.println(<span class="string">"I'm "</span> + name);</div><div class="line">    &#125;</div><div class="line"></div><div class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">showDialog</span><span class="params">()</span> </span>&#123;</div><div class="line">        <span class="keyword">new</span> Handler().postDelayed(<span class="keyword">new</span> Runnable() &#123;</div><div class="line"></div><div class="line">            <span class="meta">@Override</span></div><div class="line">            <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>&#123;</div><div class="line">                ScoreAlertDialog.showDialog(MainActivity.<span class="keyword">this</span>);</div><div class="line">            &#125;</div><div class="line">        &#125;, <span class="number">2000</span>);</div><div class="line">    &#125;</div><div class="line"></div><div class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="class"><span class="keyword">class</span> <span class="title">ScoreAlertDialog</span> </span>&#123;</div><div class="line"></div><div class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">showDialog</span><span class="params">(<span class="keyword">final</span> Activity activity)</span> </span>&#123;</div><div class="line">            <span class="keyword">if</span> (activity.isFinishing()) &#123;</div><div class="line">                <span class="keyword">return</span>;</div><div class="line">            &#125;</div><div class="line">            <span class="keyword">try</span> &#123;</div><div class="line">                AlertDialog.Builder builder = <span class="keyword">new</span> AlertDialog.Builder(activity);</div><div class="line">                builder.setTitle(<span class="string">"alert_title"</span>);</div><div class="line">                builder.setNegativeButton(<span class="string">"cancel"</span>, <span class="keyword">null</span>);</div><div class="line">                builder.setPositiveButton(<span class="string">"submit"</span>, <span class="keyword">new</span> DialogInterface.OnClickListener() &#123;</div><div class="line">                    <span class="meta">@Override</span></div><div class="line">                    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onClick</span><span class="params">(DialogInterface dialog, <span class="keyword">int</span> which)</span> </span>&#123;</div><div class="line">                        <span class="keyword">try</span> &#123;</div><div class="line">                            Toast.makeText(activity, <span class="string">"Welcome"</span>, Toast.LENGTH_LONG).show();</div><div class="line">                        &#125; <span class="keyword">catch</span> (Exception e) &#123;</div><div class="line">                            e.printStackTrace();</div><div class="line">                        &#125;</div><div class="line">                    &#125;</div><div class="line">                &#125;);</div><div class="line">                builder.show();</div><div class="line">            &#125; <span class="keyword">catch</span> (Exception e) &#123;</div><div class="line">                e.printStackTrace();</div><div class="line">            &#125;</div><div class="line">        &#125;</div><div class="line">    &#125;</div><div class="line"></div><div class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">testError</span><span class="params">()</span> </span>&#123;</div><div class="line">        <span class="keyword">try</span> &#123;</div><div class="line">            <span class="keyword">int</span> error = <span class="number">1</span> / <span class="number">0</span>;</div><div class="line">        &#125; <span class="keyword">catch</span> (Exception e) &#123;</div><div class="line">            e.printStackTrace();</div><div class="line">        &#125;</div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>打包，反编译，最后我们得到如下的代码：</p>
<p><img src="http://img.blog.csdn.net/20140928202555529?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvdHRkZXZz/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="">  </p>
<p>分析上面的代码我们会发现，自定义的方法名都被替换成无特殊意义的短字母，而activity的onCreate()方法却没变；最后一个testError()方法由于我们没有调用也被剔除掉了。这些就是默认的混淆处理策略。看到这里，感觉混淆还是小case的哈～～</p>
<p>继续往下，我们将注销的testError()打开，打包运行这个时候会报错，错误信息如下：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div></pre></td><td class="code"><pre><div class="line">java.lang.ArithmeticException: divide by zero</div><div class="line">	at com.ttdevs.proguard.MainActivity.b(Unknown Source)</div><div class="line">	at com.ttdevs.proguard.MainActivity.onCreate(Unknown Source)</div><div class="line">	at android.app.Activity.performCreate(Activity.java:<span class="number">4531</span>)</div><div class="line">	at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:<span class="number">1071</span>)</div><div class="line">	at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:<span class="number">2150</span>)</div><div class="line">	at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:<span class="number">2229</span>)</div><div class="line">	at android.app.ActivityThread.access$<span class="number">600</span>(ActivityThread.java:<span class="number">139</span>)</div><div class="line">	at android.app.ActivityThread$H.handleMessage(ActivityThread.java:<span class="number">1261</span>)</div><div class="line">	at android.os.Handler.dispatchMessage(Handler.java:<span class="number">99</span>)</div><div class="line">	at android.os.Looper.loop(Looper.java:<span class="number">154</span>)</div><div class="line">	at android.app.ActivityThread.main(ActivityThread.java:<span class="number">4945</span>)</div><div class="line">	at java.lang.reflect.Method.invokeNative(Native Method)</div><div class="line">	at java.lang.reflect.Method.invoke(Method.java:<span class="number">511</span>)</div><div class="line">	at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:<span class="number">784</span>)</div><div class="line">	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:<span class="number">551</span>)</div><div class="line">	at dalvik.system.NativeStart.main(Native Method)</div></pre></td></tr></table></figure>
<p>由于这个例子比较简单，很容易看出来是何地方出了问题，不过还是可以用来说明我们想表达的问题：如何还原混淆后的代码的错误信息？<br>为了达到这个目的，我们需要三个文件:</p>
<ul>
<li>android-sdk-windows\tools\proguard\bin\retrace.bat</li>
<li>mapping.txt</li>
<li>log.info（上面的错误信息）</li>
</ul>
<p>然后执行下面的命令（window系统）</p>
<p><code>retrace.bat mapping.txt log.txt</code></p>
<p><img src="http://img.blog.csdn.net/20140928221717551?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvdHRkZXZz/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="">  </p>
<p>从上图中可以很清楚的看到错误日志中的b()方法为我们实际代码中的setName()方法。</p>
<p>这里需要注意的是每次导出apk都会在项目中目录下的proguard文件夹下生成一个对应的mapping.txt 文件，所以对于每个apk我们都需要保存与之对应的mapping.txt文件。至此整个混淆的流程介绍完毕。</p>
<h3 id="参考"><a href="/1970/01/01/[Android]对App进行代码混淆/#参考" class="headerlink" title="参考"></a>参考</h3><ul>
<li>官方文档</li>
</ul>
<p><a href="http://developer.android.com/tools/help/proguard.html" target="_blank" rel="external">http://developer.android.com/tools/help/proguard.html</a></p>
<ul>
<li>官方文档的翻译</li>
</ul>
<p><a href="http://www.cnblogs.com/over140/archive/2011/04/22/2024528.html" target="_blank" rel="external">http://www.cnblogs.com/over140/archive/2011/04/22/2024528.html</a></p>
<p>（本想自己去翻一个，结果发现很久以前农民伯伯已经翻译，在此直接引用并感谢之）</p>
<h2 id="0x02-第二部分"><a href="/1970/01/01/[Android]对App进行代码混淆/#0x02-第二部分" class="headerlink" title="0x02 第二部分"></a>0x02 第二部分</h2><p>第一部分讲了如何操作，参照官方文档，基本都会掌握。剩下的也是最难的就是 <code>proguard-project.txt</code> 文件的编写。对于这部分，两种处理策略：自己编写<br>和使用别人写好的。先说如何使用别人写好的，我们引用的第三方库无论开源还是闭源如有特殊情况我们都可以在他的User Guide中找到混淆代码的配置，如我们引用的大名鼎鼎的 <a href="https://github.com/guillep/PullToRefresh" target="_blank" rel="external">guillep PullToRefresh</a> ，我们可以在<a href="https://github.com/guillep/PullToRefresh/blob/master/PullToRefresh/proguard.cfg" target="_blank" rel="external">他的文档</a> 中找到如下的代码：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div></pre></td><td class="code"><pre><div class="line">-optimizationpasses <span class="number">5</span></div><div class="line">-dontusemixedcaseclassnames</div><div class="line">-dontskipnonpubliclibraryclasses</div><div class="line">-dontpreverify</div><div class="line">-verbose</div><div class="line">-optimizations !code/simplification/arithmetic,!field<span class="comment">/*,!class/merging/*</span></div><div class="line">    </div><div class="line">-keep public class * extends android.app.Activity</div><div class="line">-keep public class * extends android.app.Application</div><div class="line">-keep public class * extends android.app.Service</div><div class="line">-keep public class * extends android.content.BroadcastReceiver</div><div class="line">-keep public class * extends android.content.ContentProvider</div><div class="line">-keep public class * extends android.app.backup.BackupAgentHelper</div><div class="line">-keep public class * extends android.preference.Preference</div><div class="line">-keep public class com.android.vending.licensing.ILicensingService</div><div class="line">    </div><div class="line">-keepclasseswithmembernames class * &#123;</div><div class="line">   native &lt;methods&gt;;</div><div class="line">&#125;</div><div class="line">    </div><div class="line">-keepclasseswithmembernames class * &#123;</div><div class="line">   public &lt;init&gt;(android.content.Context, android.util.AttributeSet);</div><div class="line">&#125;</div><div class="line">    </div><div class="line">-keepclasseswithmembernames class * &#123;</div><div class="line">   public &lt;init&gt;(android.content.Context, android.util.AttributeSet, int);</div><div class="line">&#125;</div><div class="line">    </div><div class="line">-keepclassmembers enum * &#123;</div><div class="line">   public static **[] values();</div><div class="line">   public static ** valueOf(java.lang.String);</div><div class="line">&#125;</div><div class="line">    </div><div class="line">-keep class * implements android.os.Parcelable &#123;</div><div class="line"> public static final android.os.Parcelable$Creator *;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>有了这部分代码我们就可以直接copy插入我们的项目中即可。这种方式还是copy式的。那下面我们举个小例子看看如何自己写代码控制是否混淆。还是用第一部分的例子，我们在这个项目的 proguard-project.txt 文件中（之前为空）加入如下几行（proguard-project.txt中“#”代表注释）：</p>
<figure class="highlight"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line"># -keep public class com.ttdevs.proguard.** &#123; *; &#125;</div><div class="line"># -keepclasseswithmembers public class com.ttdevs.proguard.** &#123; *; &#125;</div><div class="line">    </div><div class="line">-keep public class com.ttdevs.proguard.MainActivity &#123;</div><div class="line">java.lang.String getString(java.lang.String);</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>然后我们导出apk再反编译，得到如下代码：</p>
<p><img src="http://img.blog.csdn.net/20140928225529585?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvdHRkZXZz/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="">  </p>
<p>和之前的对比，我们发现其中的getString方法没有被混淆。没错，上面 proguard-project.txt的意思就是保持MainActivity的getString()方法不要被混淆。大家也可以试试上述混淆代码中被注释的两行分别是什么效果。</p>
<p>讲到这里已经开始涉及ProGuard的核心部分了，剩下的就是研读 <a href="http://proguard.sourceforge.net" target="_blank" rel="external">ProGuard的文档</a> ，掌握的他的语法并使用之。本想找一个完整的ProGuard的翻译文档，但是找了N久没有发现一个，而且连零零散散的翻译也非常的少，最近时间很紧，加之能力有限，想翻译一下常用的几个命令也是很困，所以细读的想法只能暂时往后推了。这里先简单介绍下keep命令：</p>
<p><a href="http://proguard.sourceforge.net/manual/usage.html#keep" target="_blank" rel="external">-keep [,modifier,…] class_specification</a></p>
<p>在你的代码中指定作为切入点而被保留的类或者类的成员（属性和方法）。例如，为了保持一个应用，你可以指定主类和他的main方法。为了处理一个库，你需要详细说明他的public访问的元素。  </p>
<p><a href="http://proguard.sourceforge.net/manual/usage.html#keepoverview" target="_blank" rel="external">另外还有keep的简单概述</a> 和 <a href="http://proguard.sourceforge.net/manual/usage.html#classspecification" target="_blank" rel="external">语法中规范</a> 。</p>
<p>Class Specification中会告诉你如何表示构造方法，属性和方法，<code>*</code> 与<code>**</code>的区别等等。比如 <code>*</code> 表示匹配任何的类名但是不包括包的分隔符，而 <code>**</code> 则是匹配任何的类名并且包括任意数量的包分隔符，因此上面我们注释掉的代码意思如下：</p>
<ul>
<li><p>第一行 </p>
<p>  保持com.ttdevs.proguard下的所有类和子包下的类的所有方法都不混淆</p>
</li>
<li><p>第二行</p>
<p>  保持com.ttdevs.proguard下的所有类和子包下的类的所有方法和成员变量都不混淆。</p>
</li>
</ul>
<p>// TODO 细节还有很多，比如-libraryjars、-dontwarn、-keepattributes等等，这些待续吧</p>
<h3 id="参考-1"><a href="/1970/01/01/[Android]对App进行代码混淆/#参考-1" class="headerlink" title="参考"></a>参考</h3><ul>
<li><p>ProGuard 5.0</p>
<p>  <a href="http://proguard.sourceforge.net/" target="_blank" rel="external">ProGuard 5.0</a> </p>
</li>
<li><p>ProGuard 4.7</p>
<p>  <a href="https://stuff.mit.edu/afs/sipb/project/android/sdk/android-sdk-linux/tools/proguard/docs/index.html" target="_blank" rel="external">ProGuard 4.7</a> </p>
</li>
</ul>
<p><img src="https://raw.githubusercontent.com/ttdevs/ttdevs.github.io/common/images/logo.png" alt="Create by ttdevs"></p>

          </section>
        </article>
        

       
        <div class="pager">
          
            <a class="post-prev pager-item" href="/1970/01/01/[Android]滚轮刻度尺的实现/" >
              <strong class="article-nav-caption">上一篇</strong>
              <p class="post-nav-title">「Android」滚轮刻度尺的实现</p>
            </a>
          
          
            <a class="post-next pager-item" href="/1970/01/01/[Flask]0x01FlaskMail/">
              <strong class="article-nav-caption">下一篇</strong>
              <p class="post-nav-title">「Flask」0x01FlaskMail</p>
            </a>
          
        </div>
        

         <!-- comments -->
        <div class="comment-section">
  
    


</div>

    </div>
    
    <aside>
        <div id="toc">
        </div>
    </aside>
    
</div>

                </div>
            </div>
        </div>
        <footer class="footer">
    <p>由<a href="http://hexo.io/" target="_blank">Hexo</a>强力驱动，搭载<a href="https://github.com/wayou/hexo-theme-gstyle">gstyle</a>主题</p>
    <p>
        &copy; 2017 ttdevs
    </p>
</footer>
<script src="/lib/jquery.js"></script>
<script src="/lib/waves.js"></script>
<script src="/lib/jquery-ui.js"></script>
<script src="/lib/jquery.tocify.js"></script>
<script src="/js/main.js"></script>

    </body>
</html>
